
#include "ShellParser.h"
#include <getopt.h>
#include <stdio.h>
#include <cstdio>
#include <stdlib.h>
#include <vector>
#include "../utils/Constantes.h"
#include "DynamicShellParser.h"
#include "../query/Query.h"

using std::cout;
using std::endl;
using std::exception;
using std::vector;
using namespace convertTypes;

static const short int CREATE_INDEX = 0;
static const short int QUERY_INDEX = 1;
static const short int DYNAMIC_QUERY = 2;

static struct option long_options[] = {
		{ "help", 		0, 0, 'h' },
		{ "version", 	0, 0, 'v' },
		{ "index",	 	1, 0, 'i' },
		{ "query", 		1, 0, 'q' },
		{ "repo", 		1, 0, 'r' },
		{ "dynamic",	0, 0, 'd' },
		{  0, 			0, 0,  0  }
};
const char* modifiers = "hviqrd";

ShellParser::ShellParser() {
	parser = new Parser();
}

void ShellParser::borrarIndiceAnterior(string rutaCarpetaCompleta,DIR* dirCarpetaIdx){

	struct dirent* entry;
	std::string nombreArchCompleto(rutaCarpetaCompleta);

	entry = readdir(dirCarpetaIdx);
	while(entry != NULL){
		nombreArchCompleto.append(entry->d_name);
		if(strstr(entry->d_name,DOC_DATA) != NULL){
			if(remove(nombreArchCompleto.c_str()) != OK)
				std::cerr << "[ERROR][ELIMINAR_ARCH_IDX]: \"" << entry->d_name << "\" no pudo ser eliminado." << std::endl;
		}else if(strstr(entry->d_name,DOC_F_C) != NULL){
				if(remove(nombreArchCompleto.c_str()) != OK)
					std::cerr << "[ERROR][ELIMINAR_ARCH_IDX]: \"" << entry->d_name << "\" no pudo ser eliminado." << std::endl;
		}else if(strstr(entry->d_name,DOC_NAMES_TABLE) != NULL){
				if(remove(nombreArchCompleto.c_str()) != OK)
					std::cerr << "[ERROR][ELIMINAR_ARCH_IDX]: \"" << entry->d_name << "\" no pudo ser eliminado." << std::endl;
		}else if(strstr(entry->d_name,DOC_WORDS_POINTERS) != NULL){
			if(remove(nombreArchCompleto.c_str()) != OK)
				std::cerr << "[ERROR][ELIMINAR_ARCH_IDX]: \"" << entry->d_name << "\" no pudo ser eliminado." << std::endl;
		}
		nombreArchCompleto.assign("");
		nombreArchCompleto.assign(rutaCarpetaCompleta);
		entry = readdir(dirCarpetaIdx);
	}
}

void ShellParser::crearCarpetaAlmacenamientoIndex(string& nombreRepo){

	DIR* dir;
	string rutaCompleta(REFERENCIA_CREACION_CARPETA_INDICE);
	int status;

	//Armo la ruta de la carpeta que va a contener el indice de este repo.
	rutaCompleta.append(nombreRepo);
	rutaCompleta = appendSlash(rutaCompleta);
	//Verifico existencia de la carpeta donde voy a guardar _partMerge.dat
	dir = opendir(rutaCompleta.c_str());
	if(dir != NULL){
		this->borrarIndiceAnterior(rutaCompleta,dir);
		closedir(dir);
	}
	else{//no existe el directorio, lo que debería de ser.
		//Lo creo como carpeta (1er param);
		//para lectura,escritura,ejecucion,busqueda por owner (2do param)
		//para lectura,escritura,ejecucion,busqueda por otros (3er param)
		status = mkdir(rutaCompleta.c_str(),S_IFDIR | S_IRWXU | S_IRWXO);
		if (status != 0)
			std::cerr << "[ERROR][CREACION_CARPETA_PARA_INDICE]: \"" << rutaCompleta << "\" no pudo ser creada. " << std::endl;
	}
}

/* OK */
int ShellParser::queryCheckExistenciaCarpetaIdx(string nombreDelRepoAConsultar){

	nombreDelRepoAConsultar = removeSlash(nombreDelRepoAConsultar); // le tengo que sacar la ultima barra para poder comprobar si existe la carpeta.

	string rutaPadreRepo(REFERENCIA_CREACION_CARPETA_INDICE);
	DIR* dir;
	struct dirent* entry;
	bool nombreRepoEncontrado = false;

	dir = opendir(rutaPadreRepo.c_str());
	if(dir == NULL){
		if(DEBUG > 0)
			std::cout << "[ERROR][APERTURA_CARPETA_CONTENEDORA_REPOSITORIO]: \"" << rutaPadreRepo << "\"." << std::endl;
		return ERROR;
	}else{
		entry = readdir(dir);
		while(entry != NULL && !nombreRepoEncontrado){
			if (entry->d_type == DT_DIR){// Si es una carpeta, veo si es la que estoy buscando.
				if(strstr(entry->d_name,nombreDelRepoAConsultar.c_str()) != NULL ){
					nombreRepoEncontrado = true;
				}
			}
			entry = readdir(dir);
		}
		if(closedir(dir))
			std::cerr << "[ERROR][CERRADO_CARPETA_CONTENEDORA_REPOSITORIO]: \"" << rutaPadreRepo << "\"." << std::endl;
	}
	if(nombreRepoEncontrado == true)
		return OK;
	else
		return ERROR;
}

//* OK */
int ShellParser::indexCheckRutaRepositorio(char* directory){

	DIR* dir;

	dir = opendir(directory);
	if(dir == NULL){
		std::cout << "*** [ERROR] Verifique la ruta del repositorio! ***" << endl;
		std::cout << "Ruta del repositorio ingresada: \"" << directory << "\" " << std::endl;
		return ERROR;
	}else{
		if(closedir(dir))
			std::cerr << "[ERROR][CERRADO_CARPETA_CONTENEDORA_REPOSITORIO]: \"" << directory << "\"." << std::endl;
		return OK;
	}
}


int ShellParser::getOptions(int argc, char** argv) {
	if (argc < 2) {
		cout << "Pocos parámetros." << endl;
		help();
		return EXIT_FAILURE;
	}
	clock_t chrono = clock();

	int option_index = 0;
	int c;
	opterr = 0;
	vector<short int> *opciones = new vector<short int>();

	string repo = string("");
	char* directory = (char*)"";
	char* query = (char*)"";


	while ((c = getopt_long_only(argc, argv, modifiers, long_options, &option_index)) != EOF) {

		switch (c) {
		case 'h':
			this->help();
			delete opciones;
			return EXIT_SUCCESS;
			break;
		case 'v':
			this->version();
			delete opciones;
			return EXIT_SUCCESS;
			break;
		case 'i':
			opciones->push_back(CREATE_INDEX);
			directory = optarg;
			if( this->indexCheckRutaRepositorio(directory) == ERROR){
				opciones->clear();
				delete opciones;
				return EXIT_FAILURE;
			}
//			printArgument(c, (char*)"index");
			break;
		case 'q':
			opciones->push_back(QUERY_INDEX);
			query = optarg;
//			printArgument(c, (char*)"query");
			break;
		case 'r':
			repo.assign(optarg);
			this->setRutaCarpetaIdx(repo);
//			printArgument(c, (char*)"repo");
			break;
		case 'd':
			//iniciar modo dinamico
			opciones->push_back(DYNAMIC_QUERY);
			break;
		case '?':
			cout << "Parámetro inexistente, si seguís escribiendo así te vas a ir a la B como River e Independiente.\n\n";
			help();
			return EXIT_FAILURE;
			break;
		default:
			help();
			return EXIT_SUCCESS;
			break;
		}
	}

	if (opciones->size() == 1) {
		if(repo.empty()) {
			printf("Debe ingresar un nombre de repositorio\n\n");
			help();
		}
		Query* q;
		vector<QueryToken>* parsedQuery;
	
		NWayMerge* merge;
		unsigned int wordsNumber, i;
		string file(LSI_DIRECTORY);
		char repoParaMerge[ANCHO_DIRECTORIO_ARCH_IDX_REPO] = CARPETA_ARCHS_A_MERGEAR;
		Index* index;
		unsigned int bloques;
		vector<unsigned int> * result = new vector<unsigned int>();
		vector<Match>* matchs;
		clock_t chrono1;

		switch (opciones->front()) {
		case CREATE_INDEX:
			cout << "Parseando los archivos... " << endl;
			this->crearCarpetaAlmacenamientoIndex(repo);
			parser->parseDirectory(directory,this->rutaDelRepo);

			merge = new NWayMerge(repoParaMerge);
			merge->mergeDeNVias();
			delete merge;

			cout << "Comprimiendo los archivos... " << endl;
			file.append(NOMBRE_ARCH_MAT_INICIAL);
			if (DEBUG > 0)
				cout << "Nombre del archivo a levantar el indice completo: " << file << endl;

			index = new Index(file,this->rutaDelRepo);
			index->run();
			delete index;

			chrono1 = clock();
			cout << "Tiempo en indexar los archivos: " << ((float)chrono1-(float)chrono)/ CLOCKS_PER_SEC << " s." << endl;

			break;
		case QUERY_INDEX:
			if( this->queryCheckExistenciaCarpetaIdx(repo) != OK){
				cout << "*** Nombre repositorio inexistente! ***" << endl << "\"" << repo << "\" repositorio no encontrado." << endl;
				//Libero memoria pedida en "q" y "r"
				opciones->clear();
				delete opciones;
				result->clear();
				delete result;
				return EXIT_FAILURE;
			} else if (query == NULL) {
				cout << "Parámetro faltante en -q\n" << endl;
				printf(
						"\n\tAlgunos ejemplos sobre como usar el programa"
						"\n\n\t./orga --help"
						"\n\n\t./orga --query \"Frase a buscar\" --repo \"Nombre del indice a consultar\""
						"\n\n\t./orga -q=\"Frase a buscar\" --repo \"Nombre del indice a consultar\""
						"\n"
				);
				return EXIT_FAILURE;
			}
			repo = appendSlash(repo);
			q = new Query(rutaDelRepo);

			parsedQuery = parser->parseQuery(query);
			q->fillDatas(parsedQuery);

//			printQueryTokens(parsedQuery);
			matchs = q->resolveQuery(parsedQuery);
			//matchs puede venir vacio en el caso en que una de las palabras no este en ningun documento
			if (matchs->size()!= 0){
				qsort ((void*)&(matchs->at(0)), matchs->size(), sizeof(Match), cmp_match); //ordeno los matchs por la longitud de match
//				for (unsigned int i=0; i<matchs->size(); i++){
//					result->push_back(matchs->at(i).document);
//				}

				printTimeToFindNResults(chrono, matchs->size());
//				q->printDocumentsNames(result);
				q->printDocumentsNames(matchs);
			} else {
				cout << "No se encontraron resultados para su búsqueda.\n"
						"Verifique haber ingresado las palabras correctamente." << endl;
			}

			// limpio parsedQuery
//			cleanVectorQueryToken(parsedQuery);
			parsedQuery->clear();
			delete parsedQuery;
			//Limpio matchs
			if(matchs->empty() == false){
				matchs->clear();
			}
			delete matchs;
			if(result->empty() == false)
				result->clear();
			delete result;
			delete q;
			break;

		case DYNAMIC_QUERY:
			if( this->queryCheckExistenciaCarpetaIdx(repo) != OK) {
				cout << "*** Nombre repositorio inexistente! ***" << endl << "\"" << repo << "\" repositorio no encontrado." << endl;
				opciones->clear();
				delete opciones;
				result->clear();
				delete result;
				return EXIT_FAILURE;
			}

			DynamicShellParser * dsi = new DynamicShellParser(rutaDelRepo);
			dsi->run();
			delete dsi;
			break;
		}
	} else {
		if (opciones->size() > 1)
			printf("Demasiados parámetros\n\n");
		else if (opciones->size() == 0)
			printf("No ha elegido ninguna opción\n\n");
		help();
	}

	opciones->clear();
	delete opciones;
	return EXIT_SUCCESS;
}

void ShellParser::help() {
	printf("\norga\t\t\t\t\t\t\tUser Commands"
			"\n\nNOMBRE:"
			"\n\torga - Indexa archivos y realiza consultas."

			"\n\nDESCRIPCION: "
			"\n\tListado de comandos para utilizar este programa"
			"\n\n\t-h o --help "
			"\n\t\tMuestra esta ayuda y termina"
			"\n\n\t-v o --version"
			"\n\t\tMuestra la versión del programa y termina"
			"\n\n\t-i o --index [directorio raiz]"
			"\n\t\tCrea el índice de todos los archivos dentro del directorio raíz"
			"\n\t\t[directorio raiz] "
			"\n\n\t-q o --query [consulta]"
			"\n\t\tBusca los archivos que contienen la consulta pasada como parámetro"
			"\n\t\t[consulta] Frase exacta a buscar"

			"\n\n\t-r o --repo [nombre del repositorio]"
			"\n\t\tNombre del índice a crear o consultar"

			"\n\n\t-d o --dynamic"
			"\n\t\tInicia el modo para realizar consultas dinámicamente en el cual se pueden realizar sucesivas consultas"
			" experimentando un notable aumento de velocidad de respuesta"

			"\n\nEJEMPLOS: "
			"\n\tAlgunos ejemplos sobre como usar el programa"
			"\n\n\t./orga --help"
			"\n\n\t./orga --version"
			"\n\n\t./orga --index \"Directorio raiz\" --repo \"Nombre del indice a consultar\""
			"\n\n\t./orga --query \"Frase a buscar\" --repo \"Nombre del indice a consultar\""
			"\n"
	);
}

void ShellParser::version() {
	printf("Organizacion de Datos - 7506\n\t\tTP0 - Version 1.0 \n\t\tGrupo número: 1");
}

void ShellParser::printArgument (char option, char* optionName) {
	if (optarg != NULL) {
		cout << optionName << ": " << optarg << endl;
	} else {
		cout << "No argument en " << option << endl;
	}
}

ShellParser::~ShellParser() {
	delete parser;
}

